<?php
/*
basic MVC interface,
using external files for SQL query templates and view templates,
loading custom parameters from INI files

file version 14/09/2005 14:01:07
*/
class MVCInterface {
/*--------------------------------------------------------------------------------------------------
                                                                                         constructor
--------------------------------------------------------------------------------------------------*/
	function MVCInterface() {
		// setting config
		$this->setConfig();
		//echo wakaffvar($this->cfg,'params for '.get_class($this));
		$this->data=&new datatree();
	}
/*--------------------------------------------------------------------------------------------------
                                                                                      public methods
--------------------------------------------------------------------------------------------------*/
	function & get($path=NULL) {
		return $this->data->get($path);
	}

	function getAsText($path=NULL) {
		return $this->data->getAsText($path);
	}

	function set($path,& $value) {
		return $this->data->set($path,$value);
	}

	function copy($pathFrom,$pathTo,$overWrite=true) {
		$value=$this->data->get($pathFrom);
		return $this->data->set($pathTo,$value);
	}

	function applyFunction($path,$function) {
		return $this->data->applyFunction($path,$function);
	}

	function count($path=NULL) {
		return $this->data->count($path);
	}

	function getKeys($path=NULL) {
		return $this->data->getKeys($path);
	}
	/*
	*/
	function runQuery($query,$pathFrom=NULL,$pathTo=NULL) {
		// getting query
		if (!$query=$this->getQuery($query)) return false;
		// getting data source
		if (is_string($pathFrom)) $data=$this->get($pathFrom);
		else if(is_array($pathFrom)) $data=& $pathFrom;
		else $data=NULL;
		// else $data=&$this->data;
		// echo wakaffvar($data,"data for query $query");
		// feeding query
		$query=wakTemplateFeed($query,$data);
		// running query
		$this->set('/sql/lastquery',$query);
		//echo $query;
		$result=wakSqlExecute($query);
		$this->set('/sql/lastresult',$result);
		// importing
		if ($pathTo!=NULL) $this->set($pathTo,$result);
		return $result;
	}
	function & getResult($query,$from=NULL,$indexField=NULL) {
		// retrieving query
		if (!$sql=$this->getQuery($query)) return false;
		// getting data source
		if (is_string($from)) $data=$this->get($from);
		else if(is_array($from)) $data=& $from;
		else $data=NULL;
		// feeding query
		//if (!is_array($param)) $param=array();
		if (!$sql=wakTemplateFeed($sql,$data)) return false;
		$this->set('/sql/lastquery',$sql);
		// execution
		$result=wakSqlResult($sql,$indexField);
		$this->set('/sql/lastresult',$result);
		return $result;
	}
	/*
	*/
	function & importResult($query,$pathTo='/',$pathFrom=NULL,$indexField=NULL) {
		// getting data source
		if (is_string($pathFrom)) $data=$this->data->get($pathFrom);
		else if(is_array($pathFrom)) $data=& $pathFrom;
		else $data=NULL;
		// execution
		if (!$result=$this->getResult($query,$data,$indexField)) return false;
		$this->set($pathTo,$result);
		return $result;
	}
	function & getUniqueResult($query,$param=array()) {
		// retrieving query
		if (!$sql=$this->getQuery($query)) return false;
		// feeding query
		if (!$sql=wakTemplateFeed($sql,$param)) return false;
		$this->set('/sql/lastquery',$sql);
		// execution
		$result=wakSqlUniqueResult($sql);
		$this->set('/sql/lastresult',$result);
		return $result;
	}
	/*
	*/
	function importUniqueResult($query,$pathTo='/',$pathFrom=NULL) {
		// getting data source
		if (is_string($pathFrom)) $data=$this->get($pathFrom);
		else if(is_array($pathFrom)) $data=& $pathFrom;
		else $data=NULL;
		// execution
		if (!$result=$this->getUniqueResult($query,$data)) return false;
		$this->set($pathTo,$result);
		return $result;
	}

	function getFedView($view,$pathFrom='/') {
		// getting template
		if (!$template=$this->getview($view)) return false;
		// getting data source
		if (is_string($pathFrom)) $data=$this->get($pathFrom);
		else if(is_array($pathFrom)) $data=& $pathFrom;
		else $data=NULL;
		// echo wakAffVar($data,'data for template');
		// returning fed template
		return wakTemplateFeed($template,$data);
	}

	/* import and validates http post data
	keys can either be an array of keys, or the name of a parameters-defined keysSet
	if global=true (default) all keys must valid, if not the global result will be false
	if global=false then invalid keys will be injected as NULL values within the result array
	*/
	function importValidPost($keys,$pathTo='/post',$global=true) {
		if (is_string($keys)) $keys=$this->getKeysSet($keys);
		if (!is_array($keys)) return false;
		$post=wakGetVarsIn($keys,'post');// echo wakaffvar($post,'post');
		$valid=array();
		foreach ($post as $key => $value) {
			if ($this->regexCheckKey($key,$value)===false) {
				trigger_error("$key (=$value) don't validate regex ".$this->cfg['validationRegexes'][$key]);
				if ($global) return false;
				$valid[$key]=NULL;
			} else $valid[$key]=$value;
		}
		// echo wakaffvar($valid,'valid');
		if (!$this->set($pathTo,$valid)) return false;
		// copy to pathTo.'safe', cleaning values
		$this->copy($pathTo,$pathTo.'safe');
		$this->secureValues($pathTo.'safe/*');
		// $this->showdata();
		return $valid;
	}

	/* check a set of keys upon their defined regex
	returns an array of booleans :
	for each key in the set, the same key with the boolean result of the regex
	or NULL if there is no defined regex
	*/
	function checkArray($set=array()) {
		$checkList=array();
		foreach ($set as $key => $value) {
			// echo "<p>checking $key = $value</p>";
			$check=$this->regexCheckKey($key,$value);
			// $str=$check?'ok':'ko';
			if ($check===false) trigger_error('mvcInterface: '.$key.' check is ko',E_USER_NOTICE);

			$checkList[$key]=$check;
		}
		return $checkList;
	}
	/* selects a keys set from a data source
	keys can either be an array of keys or the name a predefined keysSet
	data can either be an associative array or a path within the internal data tree
	*/
	function & getSet($keys,$data=NULL) {
		if (is_string($keys)) $keys=$this->getKeysSet($keys);
		if (!is_array($keys)) return false;
		if (is_null($data)) $data=$this->get();
		else if (is_string($data)) $data=$this->get($data,'slashes',true);
		if (!is_array($data)) return false;
		// echo affvar($data,'data source for getset');
		$set=array();
		foreach ($keys as $key) {
			if (isset($data[$key])) $set[$key]=$data[$key];
			else $set[$key]=NULL;
		}
		return $set;
	}
	/* combination of getSet and checkArray
	*/
	function checkSet($keys,$source=NULL) {
		$data=$this->getSet($keys,$source);
		return $this->checkArray($data);
	}
	function secureValues($path) {
		return $this->data->applyFunction($path,'wakSqlSecureValue');
	}

	function securePathEntities($path) {
		return $this->data->applyFunction($path,'wakXmlEntities');
	}

	function secureEntities($from) {
		if (is_array($from)) {
			for (reset($from); $key=key($from); next($from)) {
				$from[$key]=wakXmlEntities(current($from));
			}
		} else if(is_string($from)) {
			$from=wakXmlEntities($from);
		}
		return $from;
	}
/*--------------------------------------------------------------------------------------------------
                                                                                          properties
--------------------------------------------------------------------------------------------------*/

	// default parameters
	var $cfg=array(
		'general'=>array(
			'queriesPath'=>'sql/',
			'viewsPath'=>'xhtml/'
		),
		// some informations related to data structure
		'structure'=>array(
			'insertKeys'=>'' // space spearated list of keys for insertion from post
		),
		// SQL query files array : query-id => filename
		'queries'=>array(),
		// view template files array : template => filename
		'views'=>array(),
		// predefined regexes for keys validation
		'validationRegexes'=>array(),
		// predefined standard regexes
		'regexes'=>array(
			'email'=>'^[._0-9a-zA-Z-]+@[.0-9a-zA-Z-]+\.[0-9a-zA-Z]{2,4}$',
			'alphanum'=>'^[[:alnum:] [:space:]èëêéàâäîïüûùç,;\\\'-]*$',
			'num'=>'^[0-9]*$',
			'num-nn'=>'^[0-9]+$',
			'alphanum-nn'=>'^[[:alnum:][:space:]èëêéàâäîïüûùç,;\\\'-]+$',
			'text'=>'^[[:alnum:][:space:]èëêéàâäîïüûùç,;\\\.\'":€()-]+$',
			'tel'=>'^[0-9 +]*$',
			'notnull'=>'^.+$',
			'frdate'=>'^(0[1-9])|([1-2][0-9])|(3[0-1])/(0[1-9])|(1[0-2])/[0-9]{4}$',
			'year'=>'^$|^[0-9]{4}$'
		)
	);
/*--------------------------------------------------------------------------------------------------
                                                                                     private methods
--------------------------------------------------------------------------------------------------*/
	/*
	*/
	function getView($view) {
		if (!isset($this->cfg['views'][$view])) {
			trigger_error("mvc interface : view $view is not referenced");
			return false;
		}
		$file=$this->cfg['general']['viewsPath'].$this->cfg['views'][$view];
		if (!file_exists($file)) {
			trigger_error("mvc interface : view file $file don't exist");
			return false;
		}
		return wakFileContent($file);
	}

	function getQuery($query) {
		if (!isset($this->cfg['queries'][$query])) {
			trigger_error("mvc interface : query $query is not referenced",E_USER_WARNING);
			return false;
		}
		$file=$this->cfg['general']['queriesPath'].$this->cfg['queries'][$query];
		if (!file_exists($file)) {
			trigger_error("mvc interface : query file $file don't exist",E_USER_WARNING);
			return false;
		}
		return wakFileContent($file);
	}

	function getKeysSet($set) {
		if (!isset($this->cfg['keysSets'][$set])) return false;
		return explode(' ',$this->cfg['keysSets'][$set]);
	}

	/* checks a single key upon predefined regex
	can check for more than 1 regex by splitting regexes with '&&'
	*/
	function regexCheckKey($key,$value) {
		if (!isset($this->cfg['validationRegexes'][$key]) or (!is_string($value) and !is_numeric($value) and !is_null($value))) {
			// echo wakaffvar($value,$key);
			return true;
		}
		$regex=$this->cfg['validationRegexes'][$key];
		// is there more than 1 regex ?
		$regexes=split('&&',$regex);
        // multibyte regex or standard one
        // searching for multibyte extension, or using stantard func
        if (function_exists('mb_ereg')) {
            foreach ($regexes as $regex) {
                // search for predefined standard regex
                if (substr($regex,0,2)=='=>' and isset($this->cfg['regexes'][substr($regex,2)]))
                    $regex=$this->cfg['regexes'][substr($regex,2)];
                if (!mb_ereg($regex,$value)) {
                    trigger_error("$key doesn't check regex($regex), value is $value");
					return false;
				}
            }
        } else {
            foreach ($regexes as $regex) {
                // search for predefined standard regex
                if (substr($regex,0,2)=='=>' and isset($this->cfg['regexes'][substr($regex,2)]))
                    $regex=$this->cfg['regexes'][substr($regex,2)];
				if (!ereg($regex,$value)) {
					trigger_error("$key doesn't check regex($regex), value is $value");
					return false;
				}
			}
        }
		return true;
	}

	function showData() {
		echo wakaffvar($this->data->get(),'data',500);
	}

	function setConfig($cfg=NULL) {
		if (!is_array($cfg)) {
			if (isset($this->config_file))
				$iniFile=$this->config_file;
			else
				$iniFile='cfg/'.get_class($this).'.config.ini';
			// loading ini file
			if (file_exists($iniFile)) {
				$cfg=parse_ini_file($iniFile,true);// echo wakaffvar($p);
			}
		}
		// merging it with default parameters
		// echo wakaffvar($this->cfg);
		if ($cfg) $this->cfg=wakArrayMergeRR($this->cfg, $cfg);
		// echo wakaffvar($this->cfg);
		return true;
	}
}
?>
